package com.june.spider.service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import com.june.spider.exception.WebsiteNotFoundException;
import com.june.spider.model.LiveVideoRoom;

/**
 * <p>获取直播平台直播列表，目前支持斗鱼、熊猫和虎牙三大直播平台。</p>
 * <p>如果要想支持其它直播平台，请自行实现此接口中的方法。</p>
 * 
 * @param <T> 直播间实体类型
 * @author June
 */
public interface SpiderService {
	
	Logger LOG = LoggerFactory.getLogger(SpiderService.class);

	String URL_REGEX_SUFFIX = ".*";
	String DEFAULT_CHARSET_NAME = "UTF-8";

	/*========================================*/
	/* 用于解析直播列表的正则表达式中的分组名称   */
	/*========================================*/
	String GROUP_IMGURL = "imgUrl";
	String GROUP_ROOM_URL = "roomUrl";
	String GROUP_ROOM_TITLE = "roomTitle";
	String GROUP_ANCHOR = "anchorName";
	String GROUP_HEATS = "heats";

	/**
	 * 获取配置文件中的对应直播平台的正则表达式
	 * @return regex
	 */
	String getRegex();
	
	/**
	 * 获取配置文件中的对应直播平台的根网址，如https://www.douyu.com
	 * @return host
	 */
	String getHost();

	/**
	 * 根据host判断是否支持该url
	 * @param url 指定的url
	 * @return
	 */
	default boolean supportsUrl(String url) {
		return url.matches(getHost() + URL_REGEX_SUFFIX);
	}
	
	/**
	 * 根据网址获取直播间列表
	 * @param url 网址
	 * @return 直播间的列表数据
	 */
	default List<LiveVideoRoom> getRoomList(String url) {
		return parse(getHtml(url));
	}
	
	/**
	 * 解析网页内容，可以使用正则表达式或其它方式（如Jsoup工具库）进行解析
	 * @param html 网页内容
	 * @return 解析后的直播间列表数据
	 */
 	default List<LiveVideoRoom> parse(String html) {
		Matcher matcher = Pattern.compile(getRegex()).matcher(html);
		List<LiveVideoRoom> rooms = new ArrayList<>();
		LiveVideoRoom room = null;
		while (matcher.find()) {
			room = new LiveVideoRoom();
			String roomUrl = matcher.group(GROUP_ROOM_URL);
			String imgUrl = matcher.group(GROUP_IMGURL);
			room.setRoomUrl(roomUrl.startsWith(getHost()) ? roomUrl : getHost() + roomUrl);
			room.setImgUrl(imgUrl.startsWith("https:") ? imgUrl : "https:" + imgUrl);
			room.setRoomTitle(matcher.group(GROUP_ROOM_TITLE));
			room.setAnchorName(matcher.group(GROUP_ANCHOR));
			room.setHeats(matcher.group(GROUP_HEATS));
			rooms.add(room);
		}
		return rooms;
	}
	
	/**
	 * 获取指定网页的HTML文件内容
	 * @param webSiteUrl 网址
	 * @return 网页HTML文件内容
	 */
	default String getHtml(String webSiteUrl) {
		return getHtml(webSiteUrl, DEFAULT_CHARSET_NAME);
	}
	
	/**
	 * 获取网页HTML文件内容，带有请求头
	 * @param webSiteUrl 网址
	 * @param headers 请求头Map集合，如User-Agent、Referer等
	 * @return 网页HTML文件内容
	 */
	default String getHtml(String webSiteUrl, Map<String, String> headers) {
		return getHtml(webSiteUrl, DEFAULT_CHARSET_NAME, headers);
	}
	
	/**
	 * 使用给定字符集获取指定的网页内容
	 * @param webSiteUrl 网址
	 * @param charsetName 字符集名称
	 * @return 网页HTML文件内容
	 */
	default String getHtml(String webSiteUrl, String charsetName) {
		return getHtml(webSiteUrl, charsetName, null);
	}

	/**
	 * 获取网页HTML文件内容，带有字符集和请求头
	 * @param webSiteUrl 网址
	 * @param charsetName 字符集名称
	 * @param headers 请求头Map集合
	 * @return 网页HTML文件内容
	 * @throws IOException
	 */
	default String getHtml(String webSiteUrl, String charsetName, Map<String, String> headers) {
		HttpURLConnection conn = null;
		try {
			conn = (HttpURLConnection) (new URL(webSiteUrl).openConnection());
			// 添加请求头
			Optional.ofNullable(headers).orElseGet(HashMap::new).forEach(conn::setRequestProperty);
			// 打开连接
			conn.connect();
			int responseCode = conn.getResponseCode();
			if (responseCode != 200) {
				LOG.error("请求失败，响应码：" + responseCode);
				throw new WebsiteNotFoundException("未找到相关直播页面");
			}
			// 获取Content-Type中的编码
			Matcher matcher = Pattern.compile(".+?charset=(.+?)")
					.matcher(Optional.ofNullable(conn.getContentType()).orElse(""));
			String useCharsetName = matcher.matches() ? matcher.group(1) : null;
			if (StringUtils.isEmpty(useCharsetName)) {
				useCharsetName = Optional.ofNullable(charsetName).orElse(DEFAULT_CHARSET_NAME);
			}
			try (InputStream is = conn.getInputStream();
					BufferedReader reader = new BufferedReader(new InputStreamReader(is, useCharsetName))) {
				String line = null;
				StringBuilder sb = new StringBuilder();
				while ((line = reader.readLine()) != null) {
					sb.append(line);
				}
				return sb.toString();
			}
		} catch (IOException e) {
			LOG.error(e.getMessage(), e);
			return "";
		} finally {
			// 关闭连接
			conn.disconnect();
		}
	}
	
}
